#include "Test.Application.h"

#undef  APP_NAME
#define APP_NAME	TestParticles
#undef  APP_BASE
#define APP_BASE	TestApp


/*
struct ParticleVertex
{
	vec2		position;
	color4u		color;
	///
	LineVertex2D (const vec2 &pos, const color4u &color) :
		position(pos), color(color)
	{}

	static GLVertexAttribs  GetAttribs ()
	{
		return GLVertexAttribs()
			.Add( &LineVertex2D::position )
			.Add( &LineVertex2D::color )
			.SetStride( sizeof(LineVertex2D) )
			.Build();
	}

	static uint GetTypeId ()
	{
		return TypeId< LineVertex2D >();
	}
};*/


typedef Primitives::LineVertex2D	ParticleVertex;
/*
typedef Primitives::Rectangle< ParticleVertex >		ParticlePrimitive;*/

typedef Primitives::Point2D		ParticlePrimitive;


struct Particle
{
	vec2	pos;
	vec2	vel;
	bool	active;
	float	time;
	///
	Particle () : active(false), time(0.0f) {}
};


struct EmitterStrategy
{
	//typedef ParticleLineEmitter< line2d >						emitter_t;
	//typedef ParticleCircleEmitter								emitter_t;
	typedef ParticlePointEmitter								emitter_t;
	typedef ParticleEmitterDistribution< emitter_t, 32 >		distrib_t;
	typedef ParticleRandomVelocity< vec2, 64 >					rnd_vel_t;

	distrib_t	distrib;
	rnd_vel_t	rndVel;
	float		vel;
	color4u		color;
	float		lifeTime;
	uint		counter;
	///
	EmitterStrategy () :
		//distrib( emitter_t( line2d( vec2(-0.1f, 0.8f), vec2(0.1f, 0.8f) ), vec2(0.0f, -1.0f) ) ),
		//distrib( emitter_t( vec2(0.0f), 0.1f ) ),
		distrib( emitter_t( vec2(0.0f, 0.99f), 0.05f, DTR(270.0f-25.0f), DTR(270.0f+25.0f) ) ),
		lifeTime(5.0f), color(255, 0, 0, 255), vel(0.5f), counter(0)
	{
		rndVel.Generate( vec2( 0.04f ) );
		distrib.GenLinearPositins();
		//distrib.GenExponentialPositions( 3.0f, 0.5f );
		//distrib.GenExponentialVelocities( 1.0f, 0.5f );
		distrib.GenLinearVelocities();
	}

	bool Update (float dt, Particle &p)
	{
		if ( not p.active )
			return false;

		const vec2	force_pos( 0.0f, 0.85f );
		const float	force = -0.1f;

		p.time	+= dt;
		
		rndVel.Add( p.vel, ++counter, 0.2f );

		//p.vel	+= ( p.pos - force_pos ).Normalized() * force * dt;
		p.pos	+= p.vel * dt;
		p.active = p.time < lifeTime;
		return p.active;
	}

	void Draw (const Particle &p, ParticlePrimitive &pr, uint16 *indices, uint indexOffset)
	{
		ParticlePrimitive::GetIndices( indices + indexOffset * ParticlePrimitive::IndexCount(),
										indexOffset * ParticlePrimitive::VerticesCount() );

		pr = ParticlePrimitive( ParticleVertex( p.pos, color * ( p.time / lifeTime ) ) );
	}

	void Emit (Particle &p, uint index)
	{
		p = Particle();

		p.active = true;

		distrib.GetNextData( p.pos, p.vel );
		
		rndVel.Init( p.vel, distrib.GetCounter(), vel );
	}

	void Clear ()
	{
	}
};


typedef ParticleSystem< Particle, ParticlePrimitive, EmitterStrategy >		ParticleSys;



class APP_NAME : public APP_BASE
{
protected:
	ParticleSys		particleSys;


public:
	APP_NAME (IPlatform *platform, EDevice::type deviceType) :
		APP_BASE( platform, deviceType ), particleSys( ESS() )
	{}


	void Update (float dt) override
	{
		TestApp::Update( dt );

		ESS()->GetGraphicsEngine()->GetViewport()->Apply();
		ESS()->GetGLStateManager()->ClearColor( color4f( 1.0f ) );
		
		particleSys.Update( dt );
		particleSys.Draw();

		//DEBUG_CONSOLE( (string("update: particles ") << particleSys.GetNumParticles() << ", dt " << dt ).cstr() );

		
		ESS()->GetGraphicsEngine()->SwapBuffers();
	}


	void OnEnter () override
	{
		APP_BASE::OnEnter();

		BatchMaterial	mtr;
		ProgramPtr		prog;

		ESS()->GetResourceManager()->Load( EResource::PROGRAM, "prog/particle", prog );
		
		/*RenderState::ColorState	cs;
		cs.blend		= true;
		cs.blendFuncSrc	= gl_blend_func::ONE_MINUS_SRC_ALPHA;
		cs.blendFuncDst	= gl_blend_func::SRC_ALPHA;

		mtr.SetColorState( cs );*/
		mtr.SetProgram( prog );

		particleSys.SetFrequency( 500.0f );
		particleSys.SetMaxParticles( 0x7FFF );
		particleSys.SetMaterial( mtr );
	}


	void OnExit () override
	{
		particleSys.Clear();
		APP_BASE::OnExit();
	}
};